import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import ViewportOverlay from '../ViewportOverlay/ViewportOverlay.js';
import LoadingIndicator from '../LoadingIndicator/LoadingIndicator.js';
import ViewportOrientationMarkers from '../ViewportOrientationMarkers/ViewportOrientationMarkers.js';
import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cs-cornerstone-tools';
import ReactResizeDetector from 'react-resize-detector/build/withPolyfill';
import debounce from 'lodash.debounce';
import cloneDeep from 'lodash.clonedeep';
import { helpers } from '../helpers';

import './ImageViewport.css';

const scrollToIndex = cornerstoneTools.importInternal('util/scrollToIndex');
const { loadHandlerManager } = cornerstoneTools;
const { isSameOrientation } = helpers;

const AVAILABLE_TOOL_MODES = ['active', 'passive', 'enabled', 'disabled'];

const TOOL_MODE_FUNCTIONS = {
  active: cornerstoneTools.setToolActiveForElement,
  passive: cornerstoneTools.setToolPassiveForElement,
  enabled: cornerstoneTools.setToolEnabledForElement,
  disabled: cornerstoneTools.setToolDisabledForElement,
};

function ImageViewport(props) {
  const {
    useSynchronizerContext,
    onElementEnabled,
    eventListeners,
    imageIdIndex,
    context,
    startLoadHandler,
    endLoadHandler,
    loadIndicatorDelay,
    imageViewportIndex,
    activeViewportIndex,
    activeImageViewportIndex,
    isActive,
    isPlaying,
    frameRate,
    imageSync,
    showRefLine,
  } = props;

  const { synchronizers } = useSynchronizerContext();
  let loadHandlerTimeout = undefined;
  const elementRef = useRef(null);
  const [loading, setLoading] = useState({ error: null, isLoading: false });
  const [syncTrigger, setSyncTrigger] = useState(false);
  const [progress, setProgress] = useState({
    numImagesLoaded: 0,
    percentage: 0,
  });
  const [isViewportActive, setIsViewportActive] = useState(false);
  const [elementEnabled, setElementEnabled] = useState(false);
  const [viewportContext, setViewportContext] = useState(context);
  const [imageIds, setImageIds] = useState([...props.imageIds]);
  const [imageInfo, setImageInfo] = useState(null);
  const [overlayStyle, setOverlayStyle] = useState({ fontSize: '14px' });
  const [mousePos, setMousePos] = useState({ x: 0, y: 0 });

  const handleExternalEventListeners = event => {
    if (!eventListeners) {
      return;
    }

    for (let i = 0; i < eventListeners.length; i++) {
      const { eventName, handler } = eventListeners[i];

      if (event.type === eventName) {
        handler(event);
      }
    }
  };

  const handleOnElementEnabledEvent = (clear = false) => {
    setElementEnabled(!clear);
    const handler = evt => {
      const elementThatWasEnabled = evt.detail.element;
      if (elementThatWasEnabled === elementRef.current) {
        // Pass Event
        onElementEnabled(imageViewportIndex, elementThatWasEnabled);
      }
    };

    // Start Listening
    if (onElementEnabled && !clear) {
      cornerstone.events.addEventListener(
        cornerstone.EVENTS.ELEMENT_ENABLED,
        handler
      );
    }

    // Stop Listening
    if (clear) {
      cornerstone.events.removeEventListener(
        cornerstone.EVENTS.ELEMENT_ENABLED,
        handler
      );
    }
  };

  const setupLoadHandlers = (clear = false) => {
    if (clear) {
      loadHandlerManager.removeHandlers(elementRef.current);
      return;
    }

    // We use this to "flip" `isLoading` to true, if our startLoading request
    // takes longer than our "loadIndicatorDelay"
    const _startLoadHandler = element => {
      clearTimeout(loadHandlerTimeout);

      // Call user defined loadHandler
      if (startLoadHandler) {
        startLoadHandler(element);
      }

      // We're taking too long. Indicate that we're "Loading".
      loadHandlerTimeout = setTimeout(() => {
        setLoading({
          isLoading: true,
        });
      }, loadIndicatorDelay);
    };

    const _endLoadHandler = (element, image) => {
      clearTimeout(loadHandlerTimeout);

      // Call user defined loadHandler
      if (endLoadHandler) {
        endLoadHandler(element, image);
      }

      if (loading.isLoading) {
        setLoading({
          isLoading: false,
        });
      }
    };

    loadHandlerManager.setStartLoadHandler(
      _startLoadHandler,
      elementRef.current
    );
    loadHandlerManager.setEndLoadHandler(_endLoadHandler, elementRef.current);
  };

  // TODO: May need to throttle?
  const onImageRendered = event => {
    const v = event.detail.viewport;
    setLoading({ isLoading: false, error: null });
    setViewportContext(cloneDeep(v));
  };

  const onNewImageHandler = (event, callback) => {
    const { imageId } = event.detail.image;
    if (!imageId) return;
    const { sopInstanceUid } =
      cornerstone.metaData.get('generalImageModule', imageId) || {};
    const currentImageIdIndex = getToolStateImageIdIndex();

    // TODO: Should we grab and set some imageId specific metadata here?
    // Could prevent cornerstone dependencies in child components.
    // setState({ imageIdIndex: currentImageIdIndex });

    if (callback) {
      callback({ imageViewportIndex, currentImageIdIndex, sopInstanceUid });
    }
  };

  const onNewImageDebounced = debounce(event => {
    onNewImageHandler(event, props.onNewImageDebounced);
  }, props.onNewImageDebounceTime);

  const onNewImage = event => onNewImageHandler(event, props.onNewImage);

  const onImageLoaded = () => {
    setProgress({ ...progress, numImagesLoaded: progress.numImagesLoaded + 1 });
  };

  const onImageProgress = e => {
    setProgress({ ...progress, imageProgress: e.detail.percentComplete });
  };

  const setViewportActive = () => {
    if (props.setViewportActive) {
      props.setViewportActive(imageViewportIndex); // TODO: should take viewport index/ident?
    }
  };

  const onMouseDown = e => {
    const {
      detail: {
        currentPoints: { client },
      },
    } = e;
    setMousePos({ x: client.x, y: client.y });
    setViewportActive();
  };

  const onContextMenu = e => {
    if (
      mousePos.x !== e.clientX ||
      mousePos.y !== e.clientY ||
      !props.onContextMenu
    ) {
      e.preventDefault();
      return;
    }
    props.onContextMenu(e);
  };

  const resetImageInfo = () => {
    const ele = cornerstone.getEnabledElement(elementRef.current);
    if (ele && ele.image) {
      setImageInfo({
        imageId: ele.image.imageId,
        windowCenter: ele.image.windowCenter,
        windowWidth: ele.image.windowWidth,
      });
    }
  };

  const onViewportScroll = ({ detail: { direction } }) => {
    setViewportActive();

    resetImageInfo();

    if (props.onViewportScroll) {
      props.onViewportScroll(direction);
    }
  };

  const bindInternalElementEventListeners = (clear = false) => {
    const addOrRemoveEventListener = clear
      ? 'removeEventListener'
      : 'addEventListener';

    // Updates state's imageId, and imageIndex
    elementRef.current[addOrRemoveEventListener](
      cornerstone.EVENTS.NEW_IMAGE,
      onNewImage
    );

    // Updates state's imageId, and imageIndex
    elementRef.current[addOrRemoveEventListener](
      cornerstone.EVENTS.NEW_IMAGE,
      onNewImageDebounced
    );

    // Updates state's viewport
    elementRef.current[addOrRemoveEventListener](
      cornerstone.EVENTS.IMAGE_RENDERED,
      onImageRendered
    );

    // Set Viewport Active
    elementRef.current[addOrRemoveEventListener](
      cornerstoneTools.EVENTS.MOUSE_CLICK,
      setViewportActive
    );
    elementRef.current[addOrRemoveEventListener](
      cornerstoneTools.EVENTS.MOUSE_DOWN,
      onMouseDown
    );
    elementRef.current[addOrRemoveEventListener](
      cornerstoneTools.EVENTS.TOUCH_PRESS,
      setViewportActive
    );
    elementRef.current[addOrRemoveEventListener](
      cornerstoneTools.EVENTS.TOUCH_START,
      setViewportActive
    );
    // elementRef.current[addOrRemoveEventListener](
    //   cornerstoneTools.EVENTS.TAP,
    //   setViewportActive
    // );
    elementRef.current[addOrRemoveEventListener](
      cornerstoneTools.EVENTS.DOUBLE_TAP,
      props.onDoubleClick
    );
    elementRef.current[addOrRemoveEventListener](
      cornerstoneTools.EVENTS.STACK_SCROLL,
      onViewportScroll
    );
  };

  const bindExternalEventListeners = (targetType, clear = false) => {
    const addOrRemoveEventListener = clear
      ? 'removeEventListener'
      : 'addEventListener';

    // Unique list of event names
    const cornerstoneEvents = Object.values(cornerstone.EVENTS);
    const cornerstoneToolsEvents = Object.values(cornerstoneTools.EVENTS);
    const csEventNames = cornerstoneEvents.concat(cornerstoneToolsEvents);

    const targetElementOrCornerstone =
      targetType === 'element' ? elementRef.current : cornerstone.events;
    const boundMethod = handleExternalEventListeners.bind(this);

    // Bind our single handler to every cornerstone event
    for (let i = 0; i < csEventNames.length; i++) {
      targetElementOrCornerstone[addOrRemoveEventListener](
        csEventNames[i],
        boundMethod
      );
    }
  };

  const bindInternalCornerstoneEventListeners = (clear = false) => {
    const addOrRemoveEventListener = clear
      ? 'removeEventListener'
      : 'addEventListener';

    // Update image load progress
    cornerstone.events[addOrRemoveEventListener](
      'cornerstoneimageloadprogress',
      onImageProgress
    );

    // Update number of images loaded
    cornerstone.events[addOrRemoveEventListener](
      cornerstone.EVENTS.IMAGE_LOADED,
      onImageLoaded
    );
  };

  const onResize = (width, height) => {
    const size = Math.max(width, height);
    const fontSize = Math.min(Math.floor(size / 30), 14);
    setOverlayStyle({ fontSize: `${fontSize}px` });
    cornerstone.resize(elementRef.current);
  };

  const trySetActiveTool = (element, activeToolName) => {
    if (!element || !activeToolName) {
      return;
    }

    const validTools = cornerstoneTools.store.state.tools.filter(
      tool => tool.element === element
    );
    const validToolNames = validTools.map(tool => tool.name);

    if (!validToolNames.includes(activeToolName)) {
      console.warn(
        `Trying to set a tool active that is not "added". Available tools include: ${validToolNames.join(
          ', '
        )}`
      );
    }

    cornerstoneTools.setToolActiveForElement(element, activeToolName, {
      mouseButtonMask: 1,
    });
  };

  const addAndConfigureInitialToolsForElement = (tools, element) => {
    for (let i = 0; i < tools.length; i++) {
      const tool =
        typeof tools[i] === 'string'
          ? { name: tools[i] }
          : Object.assign({}, tools[i]);
      const toolName = `${tool.name}Tool`; // Top level CornerstoneTools follow this pattern

      tool.toolClass = tool.toolClass || cornerstoneTools[toolName];

      if (!tool.toolClass) {
        console.warn(`Unable to add tool with name '${tool.name}'.`);
        continue;
      }

      cornerstoneTools.addToolForElement(
        element,
        tool.toolClass,
        tool.props || {}
      );

      const hasInitialMode =
        tool.mode && AVAILABLE_TOOL_MODES.includes(tool.mode);

      if (hasInitialMode) {
        // TODO: We may need to check `tool.props` and the tool class's prototype
        // to determine the name it registered with cornerstone. `tool.name` is not
        // reliable.
        const setToolModeFn = TOOL_MODE_FUNCTIONS[tool.mode];
        setToolModeFn(element, tool.name, tool.modeOptions || {});
      }
    }
  };

  const setSynchronizer = (
    synchronizer,
    isActive,
    isViewportActive,
    doCheck = false
  ) => {
    if (isActive) {
      synchronizer.removeTarget(elementRef.current);
      const srcEles = synchronizer.getSourceElements();
      srcEles.forEach(ele => synchronizer.removeSource(ele));
      synchronizer.addSource(elementRef.current);
    } else {
      if (doCheck) {
        const srcEles = synchronizer.getSourceElements();
        const sync = srcEles.reduce(
          (sync, ele) => sync || isSameOrientation(ele, elementRef.current),
          false
        );
        if (sync) {
          synchronizer.addTarget(elementRef.current);
        } else {
          synchronizer.removeTarget(elementRef.current);
        }
      } else {
        synchronizer.addTarget(elementRef.current);
      }
    }
  };

  const removeSynchronizer = synchronizer => {
    synchronizer.removeSource(elementRef.current);
    synchronizer.removeTarget(elementRef.current);
  };

  useEffect(() => {
    if (!elementEnabled || loading.isLoading || loading.error || !syncTrigger)
      return;

    synchronizers.forEach((synchronizer, key) => {
      switch (key) {
        case 'refLineSync':
          if (showRefLine) {
            setSynchronizer(synchronizer, isActive, isViewportActive);
          } else {
            removeSynchronizer(synchronizer);
          }
          break;
        case 'positionSync':
          if (imageSync === 'position') {
            setSynchronizer(synchronizer, isActive, isViewportActive, true);
          } else {
            removeSynchronizer(synchronizer);
          }
          break;
        case 'indexSync':
          if (imageSync === 'index') {
            setSynchronizer(synchronizer, isActive, isViewportActive, true);
          } else {
            removeSynchronizer(synchronizer);
          }
          break;
      }
    });

    const enabledEle = cornerstone.getEnabledElement(elementRef.current);
    if (enabledEle.image) {
      cornerstone.updateImage(elementRef.current);
    }
  }, [
    activeViewportIndex,
    isViewportActive,
    syncTrigger,
    imageSync,
    showRefLine,
  ]);

  useEffect(() => {
    setImageIds([...props.imageIds]);
  }, [props.imageIds]);

  useEffect(() => {
    if (
      isViewportActive &&
      props.onContextChanged &&
      imageInfo &&
      viewportContext
    ) {
      const { voi } = viewportContext;
      const ele = cornerstone.getEnabledElement(elementRef.current);
      if (ele && ele.image) {
        if (ele.image.imageId === imageInfo.imageId) {
          if (
            ele.image.windowWidth !== voi.windowWidth ||
            ele.image.windowCenter !== voi.windowCenter
          ) {
            props.onContextChanged(viewportContext);
          }
        }
      }
    }
  }, [viewportContext]);

  useEffect(() => {
    if (!elementEnabled) return;
    const ele = cornerstone.getEnabledElement(elementRef.current);
    if (ele && ele.image) {
      if (imageInfo.imageId !== ele.image.imageId) {
        if (!context && viewportContext) {
          const vp = cloneDeep(viewportContext);
          vp.voi = {
            windowCenter: ele.image.windowCenter,
            windowWidth: ele.image.windowWidth,
          };
          cornerstone.setViewport(elementRef.current, vp);
        }
        resetImageInfo();
      }
    }
  }, [imageInfo]);

  useEffect(() => {
    setIsViewportActive(
      isActive && activeImageViewportIndex === imageViewportIndex
    );
  }, [isActive, activeImageViewportIndex, imageViewportIndex]);

  useEffect(() => {
    if (isViewportActive || !elementEnabled || !context) return;
    cornerstone.setViewport(elementRef.current, { ...context });
  }, [context]);

  useEffect(() => {
    if (!eventListeners) return;

    const cornerstoneEvents = Object.values(cornerstone.EVENTS);
    const cornerstoneToolsEvents = Object.values(cornerstoneTools.EVENTS);

    for (let i = 0; i < eventListeners.length; i++) {
      const { target: targetType, eventName, handler } = eventListeners[i];
      if (
        !cornerstoneEvents.includes(eventName) &&
        !cornerstoneToolsEvents.includes(eventName)
      ) {
        console.warn(
          `No cornerstone or cornerstone-tools event exists for event name: ${eventName}`
        );
        continue;
      }
    }
  }, []);

  useEffect(() => {
    // ~~ EVENTS: CORNERSTONE
    handleOnElementEnabledEvent();
    bindInternalCornerstoneEventListeners();
    bindExternalEventListeners('cornerstone');

    cornerstone.enable(elementRef.current, props.cornerstoneOptions);

    // ~~ EVENTS: ELEMENT
    bindInternalElementEventListeners();
    bindExternalEventListeners('element');

    return () => {
      const clear = true;

      handleOnElementEnabledEvent(clear);
      bindInternalCornerstoneEventListeners(clear);
      bindInternalElementEventListeners(clear);
      bindExternalEventListeners('cornerstone', clear);
      bindExternalEventListeners('element', clear);
      setupLoadHandlers(clear);

      if (props.isStackPrefetchEnabled) {
        cornerstoneTools.stackPrefetch.disable(elementRef.current);
      }

      cornerstoneTools.clearToolState(elementRef.current, 'stackPrefetch');
      cornerstoneTools.stopClip(elementRef.current);
      cornerstone.disable(elementRef.current);
    };
  }, []);

  useEffect(() => {
    scrollToIndex(elementRef.current, imageIdIndex);
    setImageInfo({ ...imageInfo });
  }, [imageIdIndex]);

  useEffect(() => {
    if (isPlaying && isViewportActive) {
      const validFrameRate = Math.max(frameRate, 1);
      cornerstoneTools.playClip(elementRef.current, validFrameRate);
    } else {
      cornerstoneTools.stopClip(elementRef.current);
    }
  }, [isPlaying, frameRate]);

  useEffect(() => {
    try {
      // Only after `uuid` is set for enabledElement
      setupLoadHandlers();

      // Setup "Stack State"
      cornerstoneTools.clearToolState(elementRef.current, 'stack');
      cornerstoneTools.addStackStateManager(elementRef.current, [
        'stack',
        'playClip',
        // 'referenceLines',
      ]);
      cornerstoneTools.addToolState(elementRef.current, 'stack', {
        imageIds: [...imageIds],
        currentImageIdIndex: imageIdIndex,
      });

      cornerstone.loadAndCacheImage(imageIds[imageIdIndex]).then(image => {
        if (!elementEnabled) return;

        cornerstone.displayImage(elementRef.current, image);

        if (props.isStackPrefetchEnabled) {
          cornerstoneTools.stackPrefetch.enable(elementRef.current);
        }

        if (isPlaying) {
          const validFrameRate = Math.max(frameRate, 1);
          cornerstoneTools.playClip(elementRef.current, validFrameRate);
        }

        addAndConfigureInitialToolsForElement(props.tools, elementRef.current);
        trySetActiveTool(elementRef.current, props.activeTool);
        cornerstone.reset(elementRef.current);
        setLoading({ isLoading: false, error: null });
        setSyncTrigger(true);

        resetImageInfo();
      });
    } catch (error) {
      setLoading({ error, isLoading: false });
    }
  }, [imageIds]);

  const getToolStateImageIdIndex = () => {
    const toolData = cornerstoneTools.getToolState(elementRef.current, 'stack');
    if (toolData && toolData.data && toolData.data.length) {
      return toolData.data[0].currentImageIdIndex;
    }
    return 0;
  };

  const getLoadingIndicator = () => {
    const { loadingIndicatorComponent: Component } = props;
    const { error, imageProgress } = progress;

    return <Component error={error} percentComplete={imageProgress} />;
  };

  const getOverlay = () => {
    if (!elementEnabled) return null;
    const {
      viewportOverlayComponent: Component,
      imageIds,
      showCornerInfo,
      showPatientInfo,
      showIrc,
    } = props;
    const imageIdIndex = getToolStateImageIdIndex();
    const v = cornerstone.getViewport(elementRef.current);
    if (!v) return null;

    const {
      scale,
      voi: { windowWidth, windowCenter },
    } = v;
    const imageId = imageIds[imageIdIndex];
    return (
      imageId &&
      windowWidth &&
      showCornerInfo && (
        <Component
          imageIndex={imageIdIndex + 1}
          stackSize={imageIds.length}
          scale={scale}
          windowWidth={windowWidth}
          windowCenter={windowCenter}
          imageId={imageId}
          showPatientInfo={showPatientInfo}
          showIrc={showIrc}
          overlayStyle={overlayStyle}
        />
      )
    );
  };

  const getOrientationMarkersOverlay = () => {
    if (!elementEnabled) return null;

    const imageIdIndex = getToolStateImageIdIndex();
    const { imageIds } = props;
    const v = cornerstone.getViewport(elementRef.current);
    if (!v) return null;

    const { rotation, vflip, hflip } = v;
    const imageId = imageIds[imageIdIndex];

    // Workaround for below TODO stub
    if (!imageId) {
      return false;
    }
    // TODO: This is throwing an error with an undefined `imageId`, and it shouldn't be
    const { rowCosines, columnCosines } =
      cornerstone.metaData.get('imagePlaneModule', imageId) || {};

    if (!rowCosines || !columnCosines || rotation === undefined) {
      return false;
    }

    return (
      <ViewportOrientationMarkers
        rowCosines={rowCosines}
        columnCosines={columnCosines}
        rotationDegrees={rotation}
        isFlippedVertically={vflip}
        isFlippedHorizontally={hflip}
        overlayStyle={overlayStyle}
      />
    );
  };

  return (
    <div
      className={classNames('image-viewport-wrapper', {
        active: isViewportActive,
      })}
    >
      {props.enableResizeDetector && (
        <ReactResizeDetector
          handleWidth
          handleHeight
          skipOnMount={false}
          refreshMode={props.resizeRefreshMode}
          refreshRate={props.resizeRefreshRateMs}
          onResize={onResize}
        />
      )}
      <div
        id={`image-viewport-${props.viewportIndex}-${props.imageViewportIndex}`}
        className="viewport-element"
        onContextMenu={onContextMenu}
        onMouseDown={e => e.preventDefault()}
        onDoubleClick={props.onDoubleClick}
        ref={elementRef}
      >
        {(loading.isLoading || loading.error) && getLoadingIndicator()}
        {/* This classname is important in that it tells `cornerstone` to not
         * create a new canvas element when we "enable" the `viewport-element`
         */}
        <canvas className="cornerstone-canvas" />
        {getOverlay()}
        {getOrientationMarkersOverlay()}
      </div>
    </div>
  );
}

ImageViewport.propTypes = {
  imageIds: PropTypes.arrayOf(PropTypes.string).isRequired,
  imageIdIndex: PropTypes.number,
  // Controlled
  activeTool: PropTypes.string,
  tools: PropTypes.arrayOf(
    PropTypes.oneOfType([
      // String
      PropTypes.string,
      // Object
      PropTypes.shape({
        name: PropTypes.string, // Tool Name
        toolClass: PropTypes.func, // Custom (ToolClass)
        props: PropTypes.Object, // Props to Pass to `addTool`
        mode: PropTypes.string, // Initial mode, if one other than default
        modeOptions: PropTypes.Object, // { mouseButtonMask: [int] }
      }),
    ])
  ),
  // Optional
  // isActive ?? classname -> active
  children: PropTypes.node,
  cornerstoneOptions: PropTypes.object, // cornerstone.enable options
  isStackPrefetchEnabled: PropTypes.bool, // should prefetch?
  // CINE
  isPlaying: PropTypes.bool,
  frameRate: PropTypes.number, // Between 1 and ?
  //
  setViewportActive: PropTypes.func, // Called when viewport should be set to active?
  onDoubleClick: PropTypes.func.isRequired,
  onViewportScroll: PropTypes.func,
  onNewImage: PropTypes.func,
  onNewImageDebounced: PropTypes.func,
  onNewImageDebounceTime: PropTypes.number,
  onContextMenu: PropTypes.func,
  viewportOverlayComponent: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.func,
  ]),
  // Cornerstone Events
  onElementEnabled: PropTypes.func, // Escape hatch
  eventListeners: PropTypes.arrayOf(
    PropTypes.shape({
      target: PropTypes.oneOf(['element', 'cornerstone']).isRequired,
      eventName: PropTypes.string.isRequired,
      handler: PropTypes.func.isRequired,
    })
  ),
  startLoadHandler: PropTypes.func,
  endLoadHandler: PropTypes.func,
  loadIndicatorDelay: PropTypes.number,
  loadingIndicatorComponent: PropTypes.oneOfType([
    PropTypes.element,
    PropTypes.func,
  ]),
  /** false to enable automatic viewport resizing */
  enableResizeDetector: PropTypes.bool,
  /** rate at witch to apply resize mode's logic */
  resizeRefreshRateMs: PropTypes.number,
  /** whether resize refresh behavior is exhibited as throttle or debounce */
  resizeRefreshMode: PropTypes.oneOf(['throttle', 'debounce']),
  //
  style: PropTypes.object,
  className: PropTypes.string,
  isActive: PropTypes.bool.isRequired,
  context: PropTypes.object,
  onContextChanged: PropTypes.func,
  viewportIndex: PropTypes.number.isRequired,
  imageViewportIndex: PropTypes.number.isRequired,
  activeViewportIndex: PropTypes.number.isRequired,
  activeImageViewportIndex: PropTypes.number.isRequired,
  useSynchronizerContext: PropTypes.func.isRequired,
  //
  showPatientInfo: PropTypes.bool,
  showIrc: PropTypes.bool,
  showCornerInfo: PropTypes.bool,
  showRefLine: PropTypes.bool,
  imageSync: PropTypes.string,
};

ImageViewport.defaultProps = {
  // Watch
  imageIdIndex: 0,
  isPlaying: false,
  frameRate: 24,
  viewportOverlayComponent: ViewportOverlay,
  imageIds: ['no-id://'],
  // Init
  cornerstoneOptions: {},
  isStackPrefetchEnabled: false,
  loadIndicatorDelay: 45,
  loadingIndicatorComponent: LoadingIndicator,
  enableResizeDetector: true,
  resizeRefreshRateMs: 100,
  resizeRefreshMode: 'debounce',
  tools: [],
  onNewImageDebounceTime: 0,
  isActive: false,
  //
  showPatientInfo: true,
  showIrc: false,
  showCornerInfo: true,
  showRefLine: true,
  imageSync: 'position',
};

export default ImageViewport;
